/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.spi.entity.info.propertyinfo;

import com.fasterxml.jackson.databind.JsonNode;
import com.inspur.edp.cef.api.manager.serialize.CefSerializeContext;
import com.inspur.edp.cef.api.repository.GspDbDataType;
import com.inspur.edp.cef.entity.changeset.ValueObjModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.FieldType;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefValueObjModelResInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.builinImpls.CefValueObjResInfo;
import com.inspur.edp.cef.spi.jsonser.util.SerializerUtil;
import com.inspur.edp.udt.api.Manager.IUdtManager;
import com.inspur.edp.udt.api.UdtManagerUtils;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

public final class ComplexUdtPropertyInfo extends UdtPropertyInfo {

    private Map<String, DataTypePropertyInfo> propertyInfos = new LinkedHashMap<>();

    public ComplexUdtPropertyInfo(String udtConfigId, boolean enableStdTimeFormat) {
        super(udtConfigId, enableStdTimeFormat);
    }

    public final Map<String, DataTypePropertyInfo> getPropertyInfos() {
        return propertyInfos;
    }

    public final void addPropertyInfo(DataTypePropertyInfo propertyInfo) {
        getPropertyInfos().put(propertyInfo.getPropertyName(), propertyInfo);
    }

    @Override
    public void setValue(Object data, String propertyName, Object value, CefSerializeContext serContext) {
        if (data instanceof ValueObjModifyChangeDetail) {
            String[] names = propertyName.split("_", 2);
            String realName = names[1];

            ValueObjModifyChangeDetail change = (ValueObjModifyChangeDetail) data;

            HashMap<String, JsonNode> nodes = new HashMap();
            nodes.put(realName, (JsonNode) value);
            context.setJsonFormatType(serContext.getJsonFormatType());
            context.setEnableStdTimeFormat(serContext.getEnableStdTimeFormat());
            context.setBigNumber(serContext.isBigNumber());

            change = (ValueObjModifyChangeDetail)SerializerUtil.readNestedChange(udtConfigId, nodes, change, context);

            return;
        }

    }

    public final void addPropertyInfo(String propertyName,String udtProeprtyName,GspDbDataType dbDataType,String dbClumnName) {
        addPropertyInfo(propertyName, "", udtProeprtyName, dbDataType, dbClumnName);
    }

    public final void addPropertyInfo(String propertyName,String udtProeprtyName) {
        addPropertyInfo(propertyName, "", udtProeprtyName);
    }

    public final void  addPropertyInfo(String propertyName,String dispayValueKey,String udtPropertyName,
        GspDbDataType dbDataType,String dbClumnName) {
        IUdtManager udtManager = (IUdtManager) UdtManagerUtils.getUdtFactory()
            .createUdtManager(this.udtConfigId);
        try {
            CefValueObjModelResInfo valueObjModelResInfo = (CefValueObjModelResInfo) udtManager
                .getClass().getMethod("getUdtModelInfo").invoke(udtManager);
            CefValueObjResInfo valueObjResInfo = (CefValueObjResInfo) valueObjModelResInfo
                .getCustomResource(valueObjModelResInfo.getRootNodeCode());
            DataTypePropertyInfo udtPropInfo = valueObjResInfo.getEntityTypeInfo()
                .getPropertyInfo(udtPropertyName);
            if (udtPropInfo == null) {
              RefDataTypePropertyInfo refDataTypePropertyInfo= new RefDataTypePropertyInfo(propertyName, dispayValueKey, false, false, 0, 0,
                  FieldType.Text, ObjectType.Normal, null, null, false, false,
                  udtPropertyName);
              refDataTypePropertyInfo.setDbColumnName(dbClumnName);
              refDataTypePropertyInfo.setDbDataType(dbDataType);
              addPropertyInfo(refDataTypePropertyInfo);
            }
            else {
              RefDataTypePropertyInfo refDataTypePropertyInfo = new RefDataTypePropertyInfo(
                  propertyName,
                  dispayValueKey, udtPropInfo.getRequired(), udtPropInfo.getEnableRtrim(),
                  udtPropInfo.getLength(), udtPropInfo.getPrecision(), udtPropInfo.getFieldType(),
                  udtPropInfo.getObjectType(), (BasePropertyInfo) udtPropInfo.getObjectInfo(),
                  udtPropInfo.getDefaultValue(), udtPropInfo.isMultiLaguange(),
                  udtPropInfo.getIsBigNumber(), udtPropertyName);
            refDataTypePropertyInfo.setDbColumnName(dbClumnName);
            refDataTypePropertyInfo.setDbDataType(dbDataType);
              addPropertyInfo(refDataTypePropertyInfo);
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException();
        } catch (InvocationTargetException e) {
            throw new RuntimeException();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException();
        }

    }

    public final void  addPropertyInfo(String propertyName,String dispayValueKey,String udtPropertyName) {
        IUdtManager udtManager = (IUdtManager) UdtManagerUtils.getUdtFactory()
            .createUdtManager(this.udtConfigId);
        try {
            CefValueObjModelResInfo valueObjModelResInfo = (CefValueObjModelResInfo) udtManager
                .getClass().getMethod("getUdtModelInfo").invoke(udtManager);
            CefValueObjResInfo valueObjResInfo = (CefValueObjResInfo) valueObjModelResInfo
                .getCustomResource(valueObjModelResInfo.getRootNodeCode());
            DataTypePropertyInfo udtPropInfo = valueObjResInfo.getEntityTypeInfo()
                .getPropertyInfo(udtPropertyName);
            if (udtPropInfo == null) {
                RefDataTypePropertyInfo refDataTypePropertyInfo= new RefDataTypePropertyInfo(propertyName, dispayValueKey, false, false, 0, 0,
                    FieldType.Text, ObjectType.Normal, null, null, false, false,
                    udtPropertyName);

                addPropertyInfo(refDataTypePropertyInfo);
            }
            else {
                RefDataTypePropertyInfo refDataTypePropertyInfo = new RefDataTypePropertyInfo(
                    propertyName,
                    dispayValueKey, udtPropInfo.getRequired(), udtPropInfo.getEnableRtrim(),
                    udtPropInfo.getLength(), udtPropInfo.getPrecision(), udtPropInfo.getFieldType(),
                    udtPropInfo.getObjectType(), (BasePropertyInfo) udtPropInfo.getObjectInfo(),
                    udtPropInfo.getDefaultValue(), udtPropInfo.isMultiLaguange(),
                    udtPropInfo.getIsBigNumber(), udtPropertyName);

                addPropertyInfo(refDataTypePropertyInfo);
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException();
        } catch (InvocationTargetException e) {
            throw new RuntimeException();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException();
        }

    }

    private  LinkedHashMap<String,String> propAndRefMapping;

    public LinkedHashMap<String, String> getPropAndRefMapping() {
        if(propAndRefMapping==null)
        {
            propAndRefMapping=new LinkedHashMap<>();
            boolean isSingle = getPropertyInfos().size() > 1 ? false: true;
            for(Map.Entry<String, DataTypePropertyInfo> refProp:getPropertyInfos().entrySet())
            {
                RefDataTypePropertyInfo refDataTypePropertyInfo= (RefDataTypePropertyInfo) refProp.getValue();
                if(isSingle){
                    propAndRefMapping.put(refDataTypePropertyInfo.getRefPropertyName(),refDataTypePropertyInfo.getDbColumnName());
                }
                else {
                    propAndRefMapping.put(refDataTypePropertyInfo.getRefPropertyName(),refDataTypePropertyInfo.getPropertyName());
                }

            }
        }
        return  propAndRefMapping;
    }


}
